﻿using Dynamicweb;
using Dynamicweb.Extensibility;
using System.Collections.Generic;

namespace dwblogengine2012.CustomModules.BlogEngine2012
{
    [AddInName("BlogEngine2012")]
    public class BlogEngine2012 : ContentModule
    {
        public override string GetContent()
        {
            switch (Properties["DisplayMode"])
            {
                case "tags":
                    return RenderTags();
                case "posts":
                default:
                    return RenderPosts();
            }
        }

        protected string RenderTags()
        {
            var tags = new /*Sorted*/Dictionary<string, IList<Dynamicweb.Content.Page>>();
            var posts = GetBlogPosts();
            var r = new System.Text.RegularExpressions.Regex("\\s*,\\s*");
            foreach (var post in posts)
            {
                foreach (var tag in GetTags(post))
                {
                    if (!tags.ContainsKey(tag))
                    {
                        tags.Add(tag, new List<Dynamicweb.Content.Page>());
                    }
                    tags[tag].Add(post);
                }
            }

            if (tags.Count == 0)
            {
                return string.Empty;
            }

            var content = new System.Text.StringBuilder();
            content.Append("<div id='tags'>");
            content.Append("<ul>");
            foreach (var tag in tags.Keys)
            {
                content.AppendFormat("<li data-count='{0}'>", tags[tag].Count);
                content.Append(GetTagLink(tag));
                content.Append("</li>");
            }
            content.Append("</ul>");
            content.Append("</div>");
            return content.ToString();
        }

        protected string RenderPosts()
        {
            var format = Base.Request("format");
            var filter = GetTagFilter();
            var posts = GetBlogPosts(filter);
            var rssItems = new List<RssItem>();
            /*
             * if ((posts == null) || (posts.Count == 0))
             {
                 return string.Empty;
             }
             */

            var paragraphs = GetParagraphs(posts);

            var templateFilename = Properties["TemplateFilename"];
            if (format == "rss")
            {
                templateFilename = Properties["TemplateFilename_rss"];
            }
            var template = new Dynamicweb.Rendering.Template(templateFilename);

            if (string.IsNullOrWhiteSpace(template.Html))
            {
                throw new System.Exception(string.Format("Empty template [{1}] (file: {0})", templateFilename, template.Html));
            }

            SetBlogTemplateTags(template);

            var itemTemplate = template.GetLoop("BlogPosts");
            foreach (var post in posts)
            {
                if (!paragraphs.ContainsKey(post.ID))
                {
                    continue;
                }


                var postParagraphs = paragraphs[post.ID];
                var paragraph = postParagraphs[0];

                var header = paragraph.Header;
                var text = string.Empty;
                for (int i = 0; (i < postParagraphs.Count) && (i < PostListMaxNumberOfParagraphs); i++)
                {
                    text += postParagraphs[i].Text;
                }
                var summary = Base.StripHTML(text);
                if (summary.Length > 200)
                {
                    summary = summary.Substring(0, 200);
                }

                var hasMoreText = postParagraphs.Count > PostListMaxNumberOfParagraphs;

                SetBlogPostTemplateTags(itemTemplate, post, header, text, summary, hasMoreText);
                itemTemplate.CommitLoop();
            }

            if (format == "rss")
            {
                Response.ClearContent();
                Response.ContentEncoding = System.Text.Encoding.UTF8;
                Response.ContentType = "application/rss+xml";
                Response.Expires = 10;
                Response.Write(template.Output());
                Response.End();
            }

            return template.Output();
        }

        private void SetBlogTemplateTags(Dynamicweb.Rendering.Template template)
        {
            var blogUrl = string.Format("{0}://{1}{2}", Request.Url.Scheme, Request.Url.Host, Dynamicweb.Frontend.SearchEngineFriendlyURLs.GetFriendlyUrl(Pageview.Page.ID));
            var requestUrl = string.Format("{0}://{1}{2}", Request.Url.Scheme, Request.Url.Host, Request.RawUrl);
            SetTag(template, "Blog:Url", requestUrl, true);
            SetTag(template, "Blog:Link", blogUrl, true);
            SetTag(template, "Blog:Title", Properties["BlogTitle"], true);
            SetTag(template, "Blog:Description", Properties["BlogDescription"], true);
            SetTag(template, "Blog:FeedTtl", Properties["FeedTtl"]);
        }

        private void SetBlogPostTemplateTags(Dynamicweb.Rendering.Template template, Dynamicweb.Content.Page post, string header, string text, string summary, bool hasMoreText)
        {
            var title = Base.ChkString(string.IsNullOrEmpty(post.MetaTitle) ? post.MenuText : post.MetaTitle);
            var author = GetPostAuthor(post);
            var permalinkUrl = string.Format("http://{0}{1}", Request.Url.Host, Dynamicweb.Frontend.SearchEngineFriendlyURLs.GetFriendlyUrl(post.ID));

            SetTag(template, "Blog:Post.Header", header, true);
            SetTag(template, "Blog:Post.Text", text);
            SetTag(template, "Blog:Post.FeedText", PrepareForFeed(text));
            SetTag(template, "Blog:Post.Summary", summary, true);
            if (hasMoreText)
            {
                SetTag(template, "Blog:Post.ReadMoreUrl", permalinkUrl);
            }

            SetTag(template, "Blog:Post.ID", post.ID);
            SetTag(template, "Blog:Post.Title", title, true);
            SetTag(template, "Blog:Post.Name", post.MenuText, true);
            SetTag(template, "Blog:Post.AuthorName", author.Name, true);
            SetTag(template, "Blog:Post.AuthorEmail", author.Email);
            SetTag(template, "Blog:Post.AuthorEmailMD5", Base.MD5HashToString(author.Email));
            SetTag(template, "Blog:Post.Created", post.CreatedDate);
            SetTag(template, "Blog:Post.Updated", post.UpdatedDate);
            SetTag(template, "Blog:Post.ActiveFrom", post.ActiveFrom);
            SetTag(template, "Blog:Post.Permalink", permalinkUrl, true);
            SetTag(template, "Blog:Post.Fulllink", permalinkUrl, true);

            var tags = GetTags(post);
            var htmlTags = new List<string>();
            var tagLinks = new List<string>();
            foreach (var tag in tags)
            {
                htmlTags.Add(Server.HtmlEncode(tag));
                tagLinks.Add(GetTagLink(tag));
            }
            if (tags.Count > 0)
            {
                template.SetTag("Blog:Post.Tags", string.Join(", ", htmlTags));
                template.SetTag("Blog:Post.Tags.Links", string.Join(", ", tagLinks));
            }
        }

        private string PrepareForFeed(string text)
        {
            // @see http://stackoverflow.com/questions/3836644/c-sharp-convert-relative-to-absolute-links-in-html-string/3836790#3836790
            text = System.Text.RegularExpressions.Regex.Replace(text, "(<[^<>]+\\s+(?:src|href)=['\"])((?![a-z]+://).)", match =>
                match.Groups[1]+GetBaseUrl()+(match.Groups[2].ToString().StartsWith("/") ? "" : "/")+match.Groups[2]
            );

            return text;
        }

        private string GetBaseUrl()
        {
            return Request.Url.Scheme + "://" + Request.Url.Host;
        }

        // Simulate Dynamicweb.Rendering.Template.SetTag in Dynamicweb 8.1.x
        private void SetTag(Dynamicweb.Rendering.Template template, string name, string value, bool htmlEncode = false)
        {
            template.SetTag(name, htmlEncode ? Server.HtmlEncode(value) : value);
        }

        private void SetTag(Dynamicweb.Rendering.Template template, string name, int value)
        {
            template.SetTag(name, value);
        }

        private void SetTag(Dynamicweb.Rendering.Template template, string name, System.DateTime value)
        {
            template.SetTag(name, value);
        }


        protected List<string> GetTags(Dynamicweb.Content.Page post)
        {
            var tags = new List<string>();
            var r = new System.Text.RegularExpressions.Regex("\\s*,\\s*");
            var tokens = r.Split(post.Keywords.Trim());
            foreach (var token in tokens)
            {
                if (!string.IsNullOrWhiteSpace(token))
                {
                    tags.Add(token);
                }
            }
            return tags;
        }

        protected string GetTagUrl(string tag)
        {
            return Dynamicweb.Frontend.SearchEngineFriendlyURLs.GetFriendlyUrl(string.Format("/Default.aspx?tag={0}", Server.UrlEncode(tag)));
        }

        protected string GetTagLink(string tag, string text = null)
        {
            return string.Format("<a href='{0}'>{1}</a>", Server.HtmlEncode(GetTagUrl(tag)), Server.HtmlEncode(text != null ? text : tag));
        }

        protected bool HasDefaultAuthor(Dynamicweb.Content.Page page)
        {
            int defaultUserID = 0;
            try
            {
                defaultUserID = int.Parse(Properties["DefaultUserID"]);
            }
            catch { }
            return (page.UserCreate == defaultUserID) || (page.UserEdit == defaultUserID);
        }

        protected Dynamicweb.Modules.UserManagement.User GetPostAuthor(Dynamicweb.Content.Page page)
        {
            var user = Dynamicweb.Modules.UserManagement.User.GetUserByID(page.UserEdit);
            if (user == null)
            {
                user = new Dynamicweb.Modules.UserManagement.User()
                {
                    Name = DefaultAuthorName,
                    Email = DefaultAuthorEMail
                };
            }
            return user;
        }

        protected string GetAuthorName(Dynamicweb.Content.Page page)
        {
            var user = GetPostAuthor(page);
            return user.Name;
            //return page.UserCreate.ToString();
            //return DefaultAuthorName;
        }

        protected string GetAuthorEmail(Dynamicweb.Content.Page page)
        {
            var user = GetPostAuthor(page);
            return user.Email;
        }

        private string DefaultAuthorName
        {
            get
            {
                return Properties["DefaultAuthorName"];
            }
        }

        private string DefaultAuthorEMail
        {
            get
            {
                return Properties["DefaultAuthorEmail"];
            }
        }

        protected System.Collections.Generic.Dictionary<int, Dynamicweb.Content.ParagraphCollection> GetParagraphs(Dynamicweb.Content.PageCollection pages)
        {
            var paragraphs = new System.Collections.Generic.Dictionary<int, Dynamicweb.Content.ParagraphCollection>();

            var pageIDs = new List<int>();
            foreach (var p in pages)
            {
                pageIDs.Add(p.ID);
            }

            if (pageIDs.Count > 0)
            {
                var select = "*";
                var from = "Paragraph";
                var where = "1=1"
                + " and (ParagraphModuleSystemName is null or ParagraphModuleSystemName = '')"
                    //+ " and ParagraphSort = 1" // Only get first paragraph
                + " and ParagraphShowParagraph = " + Database.SqlBool(true)
                + " and ParagraphPageID in (" + string.Join(",", pageIDs) + ")";
                var orderBy = "ParagraphSort";

                var sql = string.Format("select {0} from {1} where {2} order by {3}", select, from, where, orderBy);
                var collection = Dynamicweb.Content.Paragraph.GetParagraphsBySql(sql);
                foreach (var p in collection)
                {
                    if (!paragraphs.ContainsKey(p.PageID))
                    {
                        paragraphs[p.PageID] = new Dynamicweb.Content.ParagraphCollection();
                    }
                    paragraphs[p.PageID].Add(p);
                }
            }

            return paragraphs;
        }

        protected string GetTagFilter()
        {
            string filter = null;
            string tag = Request["tag"];
            if (tag != null)
            {
                tag = tag.Replace("'", "").Replace("%", "");
                filter = "(0=1";
                // Match a single tag
                filter += " or Page.PageKeywords like '" + tag + "'";
                // Match first tag
                filter += " or Page.PageKeywords like '" + tag + ",%'";
                // Match middle tag
                filter += " or Page.PageKeywords like '%," + tag + ",%'";
                filter += " or Page.PageKeywords like '%, " + tag + ",%'";
                // Match last tag
                filter += " or Page.PageKeywords like '%," + tag + "'";
                filter += " or Page.PageKeywords like '%, " + tag + "'";
                filter += ")";
            }

            // Response.Write("[filter: " + filter + "]");

            return filter;
        }

        protected Dynamicweb.Content.PageCollection GetBlogPosts(string additionalWhere = null)
        {
            var pageIds = new List<int>();
            var startLevel = Base.ChkInteger(Pageview.Page.Values["PageLevel"]);
            var childPages = GetChildPages(Pageview.Page);
            foreach (var page in childPages)
            {
                var pageLevel = Base.ChkInteger(page.Values["pagelevel"]);
                //if ((startLevel + 2 == pageLevel) || (startLevel + 3 == pageLevel))
                {
                    pageIds.Add(Base.ChkInteger(page.Values["pageid"]));
                }
            }

            if (pageIds.Count == 0)
            {
                pageIds.Add(0);
            }

            var select = "*";
            var from = "Page inner join Paragraph on ParagraphPageID = PageID";
            var where = "1=1"
            + " and (Paragraph.ParagraphModuleSystemName is null or Paragraph.ParagraphModuleSystemName = '')"
            + " and Page.PageActive = '" + Database.SqlBool(true) + "'"
            + " and Paragraph.ParagraphShowParagraph = '" + Database.SqlBool(true) + "'"
            + " and Paragraph.ParagraphSort = 1"
            + " and Page.PageActiveFrom <= " + Database.SqlDate(System.DateTime.Now)
            + " and Page.PageID in (" + string.Join(",", pageIds) + ")";

            if (additionalWhere != null)
            {
                where += " and " + additionalWhere;
            }
            var orderBy = "Page.PageActiveFrom desc";

            var limit = 10;
            try
            {
                limit = int.Parse(Properties["PostListLimit"]);
            }
            catch { }
            var sql = string.Format("select top {0} {1} from {2} where {3} order by {4}", limit, select, from, where, orderBy);

            var pages = Dynamicweb.Content.Page.GetPagesBySql(sql);
            return pages;
        }

        protected List<Dynamicweb.Frontend.Page> GetChildPages(Dynamicweb.Frontend.Page page)
        {
            var children = new List<Dynamicweb.Frontend.Page>();
            CollectChildPages(page.ID, children);
            return children;
        }

        protected void CollectChildPages(int pageID, List<Dynamicweb.Frontend.Page> children)
        {
            if (Pageview.PageCollection.ContainsKey(pageID))
            {
                var page = Pageview.PageCollection[pageID] as Dynamicweb.Frontend.Page;
                if (Base.ChkBoolean(page.Values["pagehaschildren"]))
                {
                    if (page.Values["pagechilds"] is System.Collections.ArrayList)
                    {
                        foreach (int childPageID in page.Values["pagechilds"] as System.Collections.ArrayList)
                        {
                            if (Pageview.PageCollection.ContainsKey(childPageID))
                            {
                                children.Add(Pageview.PageCollection[childPageID] as Dynamicweb.Frontend.Page);
                            }
                            CollectChildPages(childPageID, children);
                        }
                    }
                }
            }
        }

        protected class RssItem
        {
            public string Title = string.Empty;
            public string Link = string.Empty;
            public string Description = string.Empty;
            public string Author = string.Empty;
            public string Guid = string.Empty;
            public System.DateTime PubDate = System.DateTime.Now;
        }

        private int PostListMaxNumberOfParagraphs
        {
            get
            {
                try
                {
                    return int.Parse(Properties["PostListMaxNumberOfParagraphs"]);
                }
                catch
                {
                    return 1;
                }
            }
        }
    }
}

